/*-------------------------------------------------------------------------
 *
 * parser.c
 *		PostgreSQL语法的主要入口/驱动程序
 *
 * 这应与src/backend/parser/parser.c匹配，尽管我们不需要处理重入接口。
 *
 * 注意：ECPG没有像后端那样报告错误位置。
 * 如果我们希望这样做，这个文件将需要修改。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/interfaces/ecpg/preproc/parser.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres_fe.h"

#include "preproc_extern.h"
#include "preproc.h"


static bool have_lookahead;		/* 是lookahead信息有效吗? */
static int	lookahead_token;	/* 一-token lookahead */
static YYSTYPE lookahead_yylval;	/* yylval用于lookahead token */
static YYLTYPE lookahead_yylloc;	/* yylloc用于lookahead token */
static char *lookahead_yytext;	/* 开始当前token */

static bool fc_check_uescapechar(unsigned char fc_escape);
static bool fc_ecpg_isspace(char fc_ch);


/*
 * 解析器和基本词法分析器之间的中间过滤器（scan.l中的base_yylex）。
 *
 * 这个过滤器是必要的，因为在某些情况下，标准SQL语法
 * 需要多个token的lookahead。我们通过在这里替换tokens
 * 将这些情况简化为一-token lookahead，以保持文法为LALR(1)。
 *
 * 使用过滤器比直接在scan.l中识别多词tokens更简单，
 * 因为我们必须考虑单词之间的注释。此外，如何做到这一点
 * 并不清楚，而不重新引入扫描器回溯，这会比这个过滤器层
 * 花费更多的性能。
 *
 * 我们还使用这个过滤器将UIDENT和USCONST序列转换为
 * 普通的IDENT和SCONST tokens。虽然这可以通过在主文法中
 * 添加额外的产出处理，但这样做会更有效率。
 */
int filtered_base_yylex(void)
{
	int			fc_cur_token;
	int			fc_next_token;
	YYSTYPE		fc_cur_yylval;
	YYLTYPE		fc_cur_yylloc;
	char	   *fc_cur_yytext;

	/* 获取下一个token --- 我们可能已经有了它 */
	if (have_lookahead)
	{
		fc_cur_token = lookahead_token;
		base_yylval = lookahead_yylval;
		base_yylloc = lookahead_yylloc;
		base_yytext = lookahead_yytext;
		have_lookahead = false;
	}
	else
		fc_cur_token = base_yylex();

	/*
	 * 如果这个token不需要lookahead，只需返回它。
	 */
	switch (fc_cur_token)
	{
		case NOT:
		case NULLS_P:
		case WITH:
		case UIDENT:
		case USCONST:
			break;
		default:
			return fc_cur_token;
	}

	/* 在调用周围保存和恢复词法分析器输出变量 */
	fc_cur_yylval = base_yylval;
	fc_cur_yylloc = base_yylloc;
	fc_cur_yytext = base_yytext;

	/* 获取下一个token，并将输出保存到lookahead变量中 */
	fc_next_token = base_yylex();

	lookahead_token = fc_next_token;
	lookahead_yylval = base_yylval;
	lookahead_yylloc = base_yylloc;
	lookahead_yytext = base_yytext;

	base_yylval = fc_cur_yylval;
	base_yylloc = fc_cur_yylloc;
	base_yytext = fc_cur_yytext;

	have_lookahead = true;

	/* 根据lookahead替换cur_token（如果需要） */
	switch (fc_cur_token)
	{
		case NOT:
			/* 如果NOT后面跟着BETWEEN、IN等，则将NOT替换为NOT_LA */
			switch (fc_next_token)
			{
				case BETWEEN:
				case IN_P:
				case LIKE:
				case ILIKE:
				case SIMILAR:
					fc_cur_token = NOT_LA;
					break;
			}
			break;

		case NULLS_P:
			/* 如果NULLS_P后面跟着FIRST或LAST，则将NULLS_P替换为NULLS_LA */
			switch (fc_next_token)
			{
				case FIRST_P:
				case LAST_P:
					fc_cur_token = NULLS_LA;
					break;
			}
			break;

		case WITH:
			/* 如果WITH后面跟着TIME或ORDINALITY，则将WITH替换为WITH_LA */
			switch (fc_next_token)
			{
				case TIME:
				case ORDINALITY:
					fc_cur_token = WITH_LA;
					break;
			}
			break;
		case UIDENT:
		case USCONST:
			/* 查看UESCAPE */
			if (fc_next_token == UESCAPE)
			{
				/* 没错，所以获取第三个token，最好是SCONST */
				const char *fc_escstr;

				/*
				 * 再次在调用周围保存和恢复词法分析器输出变量
				 */
				fc_cur_yylval = base_yylval;
				fc_cur_yylloc = base_yylloc;
				fc_cur_yytext = base_yytext;

				/* 获取第三个token */
				fc_next_token = base_yylex();

				if (fc_next_token != SCONST)
					mmerror(PARSE_ERROR, ET_ERROR, "UESCAPE must be followed by a simple string literal");

				/*
				 * 保存并检查转义字符串，扫描器返回
				 * 时带有引号
				 */
				fc_escstr = base_yylval.str;
				if (strlen(fc_escstr) != 3 || !fc_check_uescapechar(fc_escstr[1]))
					mmerror(PARSE_ERROR, ET_ERROR, "invalid Unicode escape character");

				base_yylval = fc_cur_yylval;
				base_yylloc = fc_cur_yylloc;
				base_yytext = fc_cur_yytext;

				/* 将3个tokens合并为1个 */
				base_yylval.str = psprintf("%s UESCAPE %s", base_yylval.str, fc_escstr);

				/* 清除have_lookahead，从而消耗这三种token */
				have_lookahead = false;
			}

			if (fc_cur_token == UIDENT)
				fc_cur_token = IDENT;
			else if (fc_cur_token == USCONST)
				fc_cur_token = SCONST;
			break;
	}

	return fc_cur_token;
}

/*
 * check_uescapechar()和ecpg_isspace()应该与
 * pgc.l中的对应项匹配。
 */

/* "escape"作为Unicode转义字符（UESCAPE语法）是否可接受？ */
static bool fc_check_uescapechar(unsigned char fc_escape)
{
	if (isxdigit(fc_escape)
		|| fc_escape == '+'
		|| fc_escape == '\''
		|| fc_escape == '"'
		|| fc_ecpg_isspace(fc_escape))
		return false;
	else
		return true;
}

/*
 * ecpg_isspace() --- 如果flex扫描器认为字符是空白字符则返回true
 */
static bool fc_ecpg_isspace(char fc_ch)
{
	if (fc_ch == ' ' ||
		fc_ch == '\t' ||
		fc_ch == '\n' ||
		fc_ch == '\r' ||
		fc_ch == '\f')
		return true;
	return false;
}
